/* Copyright (C) 2007 Wesley Crossman
Email: wesley@crossmans.net

Portions Copyright (C) 2007 L. Donnie Smith <wiimote@abstrakraft.org>

You can redistribute and/or modify this software under the terms of the GNU
General Public License as published by the Free Software Foundation;
either version 2 of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE. See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with this
program; if not, write to the Free Software Foundation, Inc., 59 Temple Place,
Suite 330, Boston, MA 02111-1307 USA */

#include "WiiWindow.h"
#include "WiiLed.h"
#ifdef WIIMOTE
extern "C" {
#include <bluetooth/bluetooth.h>
#include <wiimote.h>
}
#endif
#include <QtGui>
#include <QtSvg>

QList < WiiWindow * > windows;

#ifdef WIIMOTE
wiimote_t *wiimote = NULL;
wiimote_mesg_callback_t wiimote_callback;
bdaddr_t bdaddr = *BDADDR_ANY;
#else
int wiimote = NULL;
#define WIIMOTE_BTN_2           0x0001
#define WIIMOTE_BTN_1           0x0002
#define WIIMOTE_BTN_B           0x0004
#define WIIMOTE_BTN_A           0x0008
#define WIIMOTE_BTN_MINUS       0x0010
#define WIIMOTE_BTN_HOME        0x0080
#define WIIMOTE_BTN_LEFT        0x0100
#define WIIMOTE_BTN_RIGHT       0x0200
#define WIIMOTE_BTN_DOWN        0x0400
#define WIIMOTE_BTN_UP          0x0800
#define WIIMOTE_BTN_PLUS        0x1000
#endif
volatile int buttonValue = 0;

WiiWindow::WiiWindow() {
    windows.append(this);
    setAttribute(Qt::WA_DeleteOnClose);
    setupUi(this);
    setStatusBar(NULL);
    scene = new QGraphicsScene(grvWiimote);
    grvWiimote->setScene(scene);
    scene->addItem(new QGraphicsSvgItem("wiimote.svg", NULL));
    buttons[WIIMOTE_BTN_LEFT] = new QGraphicsSvgItem("wiimote_left.svg", NULL);
    buttons[WIIMOTE_BTN_RIGHT] = new QGraphicsSvgItem("wiimote_right.svg", NULL);
    buttons[WIIMOTE_BTN_UP] = new QGraphicsSvgItem("wiimote_up.svg", NULL);
    buttons[WIIMOTE_BTN_DOWN] = new QGraphicsSvgItem("wiimote_down.svg", NULL);
    buttons[WIIMOTE_BTN_A] = new QGraphicsSvgItem("wiimote_a.svg", NULL);
    buttons[WIIMOTE_BTN_B] = new QGraphicsSvgItem("wiimote_trigger.svg", NULL);
    buttons[WIIMOTE_BTN_MINUS] = new QGraphicsSvgItem("wiimote_minus.svg", NULL);
    buttons[WIIMOTE_BTN_HOME] = new QGraphicsSvgItem("wiimote_home.svg", NULL);
    buttons[WIIMOTE_BTN_PLUS] = new QGraphicsSvgItem("wiimote_plus.svg", NULL);
    buttons[WIIMOTE_BTN_1] = new QGraphicsSvgItem("wiimote_1.svg", NULL);
    buttons[WIIMOTE_BTN_2] = new QGraphicsSvgItem("wiimote_2.svg", NULL);
    foreach (QGraphicsSvgItem *item, buttons.values()) {
        scene->addItem(item);
        item->hide();
    }
    leds.append(new WiiLed(0));
    leds[0]->setPos(57, 486);
    leds.append(new WiiLed(1));
    leds[1]->setPos(82, 486);
    leds.append(new WiiLed(2));
    leds[2]->setPos(107, 486);
    leds.append(new WiiLed(3));
    leds[3]->setPos(133, 486);
    foreach (WiiLed *led, leds) {
        scene->addItem(led);
        connect(led, SIGNAL(toggled(int, bool)), SLOT(ledToggled()));
    }

    connect(btnConnect, SIGNAL(clicked()), SLOT(connectClicked()));
    connect(btnDisconnect, SIGNAL(clicked()), SLOT(disconnectClicked()));
    connect(btnRumble, SIGNAL(clicked()), SLOT(rumbleClicked()));
    connect(&buttonTimer, SIGNAL(timeout()), SLOT(updateButtons()));
    connect(&rumbleEffectTimer, SIGNAL(timeout()), SLOT(rumbleEffectTimeout()));
    updateButtons(0);
    setButtonValue = 0;
    buttonTimer.start(50);
}

WiiWindow::~WiiWindow() {
    windows.removeAll(this);
    if (wiimote) disconnectRemote();
}

void WiiWindow::updateStatus() {
    grvWiimote->setEnabled(wiimote);
    btnConnect->setEnabled(!wiimote);
    btnDisconnect->setEnabled(wiimote);
    grpRumble->setEnabled(wiimote);
    if (!wiimote) {
        foreach (WiiLed *led, leds) {
            led->setOn(false);
        }
    }
}

bool WiiWindow::connectRemote() {
#ifdef WIIMOTE
    wiimote = wiimote_connect(bdaddr, &wiimote_callback);
#else
    wiimote = 1;
#endif
    updateStatus();
    return wiimote;
}

bool WiiWindow::disconnectRemote() {
    bool ret = false;
#ifdef WIIMOTE
    ret = wiimote_disconnect(wiimote);
#endif
    wiimote = NULL;
    updateStatus();
    return !ret;
}

void WiiWindow::connectClicked() {
    if (!connectRemote()) {
        QMessageBox::critical(NULL, "WiiWindow", tr("Connection failed."));
    }
}

void WiiWindow::disconnectClicked() {
    if (!disconnectRemote()) {
        QMessageBox::critical(NULL, "WiiWindow", tr("Disconnection failed."));
    }
}

void WiiWindow::rumbleClicked() {
#ifdef WIIMOTE
    if (wiimote_command(wiimote, WIIMOTE_RUMBLE, 1)) {
        QMessageBox::critical(NULL, "WiiWindow", tr("Rumble start failed"));
	return;
    }
#endif
    QTimer::singleShot(sldRumble->value()*1000, this, SLOT(rumbleTimeout()));
    btnRumble->setEnabled(false);
    rumbleEffectState = true;
    rumbleEffectTimer.start(75);
}

void WiiWindow::rumbleTimeout() {
#ifdef WIIMOTE
    if (wiimote_command(wiimote, WIIMOTE_RUMBLE, 0)) {
	QMessageBox::critical(NULL, "WiiWindow", tr("Rumble stop failed"));
    }
#endif
    btnRumble->setEnabled(true);
    rumbleEffectTimer.stop();
    grvWiimote->resetMatrix();
}

void WiiWindow::rumbleEffectTimeout() {
    if (rumbleEffectState) {
        grvWiimote->scale(1.01, 1.01);
    } else {
        grvWiimote->scale(0.99, 0.99);
    }
    rumbleEffectState = !rumbleEffectState;
}

void WiiWindow::ledToggled() {
#ifdef WIIMOTE
    wiimote_command(wiimote, WIIMOTE_LED,
            (leds[0]->isOn() ? WIIMOTE_LED0_ON : 0)
          | (leds[1]->isOn() ? WIIMOTE_LED1_ON : 0)
          | (leds[2]->isOn() ? WIIMOTE_LED2_ON : 0)
          | (leds[3]->isOn() ? WIIMOTE_LED3_ON : 0) );
#endif
}

void WiiWindow::updateButtons(int btnValue) {
    foreach (int mask, buttons.keys()) {
        buttons[mask]->setVisible(btnValue & mask);
    }
    setButtonValue = buttonValue;
}

void WiiWindow::updateButtons() {
    if (setButtonValue != buttonValue) {
        updateButtons(buttonValue);
    }
}

#ifdef WIIMOTE
void wiimote_callback(enum wiimote_mesg mesg, union wiimote_mesg_data *data) {
    switch (mesg) {
    case WIIMOTE_BTN:
        buttonValue = data->btn_data.buttons;
        break;
    default:
        break;
    }
}
#endif

